package main.java.com.fmanager.services.impl;

import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.stereotype.Service;

import main.java.com.fmanager.dao.UserDAO;
import main.java.com.fmanager.exception.FmanagerRestException;
import main.java.com.fmanager.models.User;
import main.java.com.fmanager.services.UserServcie;
import main.java.com.fmanager.utils.ErrorNumber;

@Service
public class UserServiceImpl implements UserServcie {

	@Value("${security.jwt.client-id}")
	private String clientId;

	@Value("${security.jwt.grant-type}")
	private String grantType;

	@Value("${security.jwt.scope-read}")
	private String scopeRead;

	@Value("${security.jwt.scope-write}")
	private String scopeWrite;

	@Resource
	private UserDAO userDAO;

	@Resource
	private AuthenticationManager authenticationManager;

	@Resource
	private AuthorizationServerTokenServices tokenServices;

	@Resource
	private ClientDetailsService clientDetailsService;

	@Override
	public User findById(long id) {
		return userDAO.findById(id);
	}

	@Override
	public OAuth2AccessToken registerUser(User user) throws FmanagerRestException {

		User u = userDAO.findByEmail(user.getEmail());
		if (u != null) {
			throw new FmanagerRestException(ErrorNumber.USER_EMAIL_EXSIT, "User email exist");
		}

		User copy = new User();
		BeanUtils.copyProperties(user, copy);
		copy.setCreateDate(new Timestamp(System.currentTimeMillis()));
		// 将得到的 md5 密码再加密一边存数据库
		// encode password
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
		copy.setPassword(encoder.encode(user.getPassword()));
		userDAO.registerUser(copy);

		return createToken(user);
	}

	private OAuth2AccessToken createToken(User user) {
		Map<String, String> param = new HashMap<>();

		param.put("username", user.getEmail());
		param.put("password", user.getPassword());
		param.put("grant_type", grantType);

		Set<String> scopes = new HashSet<>();
		scopes.add(scopeRead);
		scopes.add(scopeWrite);

		TokenRequest tokenRequest = new TokenRequest(param, clientId, scopes, grantType);
		ClientDetails authenticatedClient = clientDetailsService.loadClientByClientId(clientId);

		// user password authentication token
		Authentication userAuth = new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword());
		param.remove("password");
		((AbstractAuthenticationToken) userAuth).setDetails(param);

		userAuth = authenticationManager.authenticate(userAuth);
		return tokenServices.createAccessToken(
				new OAuth2Authentication(tokenRequest.createOAuth2Request(authenticatedClient), userAuth));
	}

	@Override
	public List<User> getUserList() {
		return userDAO.getUserList();
	}

	@Override
	public void deleteUserById(long id) {
		userDAO.deleteUserById(id);
	}

}
